package com.heima.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.heima.mapper.ApArticleMapper;
import com.heima.model.mess.app.ArticleVisitStreamMess;
import com.heima.search.service.HotArticleService;
import com.heima.common.constants.article.ArticleConstants;
import com.heima.common.exception.CustomException;
import com.heima.feigns.AdminFeign;
import com.heima.model.admin.pojos.AdChannel;
import com.heima.model.article.pojos.ApArticle;
import com.heima.model.article.vos.HotArticleVo;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.utils.common.DateUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class HotArticleServiceImpl implements HotArticleService {
    @Autowired
    ApArticleMapper apArticleMapper;
    @Autowired
    AdminFeign adminFeign;
    @Autowired
    StringRedisTemplate redisTemplate;
    /**
     * 计算文章热度
     */
    @Override
    public void computeHotArticle() {
        //1  筛选出当天前5天的文章
        String date = LocalDateTime.now().minusDays(5)
                .format(DateTimeFormatter.ofPattern("yyyy-MM-dd 00:00:00"));
        List<ApArticle> articleList = apArticleMapper.selectArticleByDate(date);
        //2  计算这些文章的热点值
        List<HotArticleVo> hotArticleVoList= computeArticleScore(articleList);
        System.out.println(hotArticleVoList);

        //3 为每一个频道缓存热点较高的30条文章
        cacheTagToRedis(hotArticleVoList);

        //4 给推荐频道缓存30条数据  所有文章排序之后的前30条
        sortAndCache(hotArticleVoList, ArticleConstants.HOT_ARTICLE_FIRST_PAGE + ArticleConstants.DEFAULT_TAG);




    }

    /**
     *  更新热度
     * @param mess
     */
    @Override
    public void updateApArticle(ArticleVisitStreamMess mess) {
        //1. 根据ID查询文章数据
        ApArticle apArticle = apArticleMapper.selectById(mess.getArticleId());
        if (apArticle == null) {
            log.error("未查询到相关文章，文章Id{}",mess.getArticleId());
            return;
        }
        //2.  更新文章   各个行为的值
        apArticle.setComment((int)(apArticle.getComment()==null?0:apArticle.getComment()+mess.getComment()));
        apArticle.setLikes((int)(apArticle.getLikes()==null?0:apArticle.getLikes()+mess.getLike()));
        apArticle.setViews((int)(apArticle.getViews()==null?0:apArticle.getViews()+mess.getView()));
        apArticle.setCollection((int)(apArticle.getCollection()==null?0:apArticle.getCollection()+mess.getCollect()));
        apArticleMapper.updateById(apArticle);
        //3.  计算得分
        Integer score = computerScore(apArticle);
        //4.  判断文章发布时间  今天*3
        String newStr = DateUtils.dateToString(new Date());
        String publishStr = DateUtils.dateToString(apArticle.getPublishTime());
        if (publishStr.equals(newStr)) {
            score*=3;
        }
        //5.  查询对应的热点列表  替换分值较低的
        updateApArticleCache(apArticle,score,ArticleConstants.HOT_ARTICLE_FIRST_PAGE+apArticle.getChannelId());
        //6.  更新推荐热点文章   替换分值较低的
        updateApArticleCache(apArticle,score,ArticleConstants.HOT_ARTICLE_FIRST_PAGE+ArticleConstants.DEFAULT_TAG);

    }

    /**
     *      更新热点文章
     * @param apArticle
     * @param score
     * @param cacheKey
     */
    private void updateApArticleCache(ApArticle apArticle, Integer score, String cacheKey) {
        boolean flag = false;
        String hotArticleListJson = redisTemplate.opsForValue().get(cacheKey);
        if (StringUtils.isNotBlank(hotArticleListJson)) {
            List<HotArticleVo> hotArticleList = JSONArray.parseArray(hotArticleListJson,HotArticleVo.class);
            //1 如果当前缓存中有当前文章，更新分值
            for (HotArticleVo hotArticleVo : hotArticleList) {
                if (hotArticleVo.getId().equals(apArticle.getId())) {
                    hotArticleVo.setScore(score);
                    flag = true;
                    break;
                }
            }
            //2 缓存中没有当前文章
            if (!flag) {
                HotArticleVo hotArticle = new HotArticleVo();
                BeanUtils.copyProperties(apArticle, hotArticle);
                hotArticle.setScore(score);
                hotArticleList.add(hotArticle);
            }
            //3. 将热点文章集合 按得分降序排序  取前30条缓存至redis中
            hotArticleList = hotArticleList.stream()
                    .sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                    .limit(30)
                    .collect(Collectors.toList());
            redisTemplate.opsForValue().set(cacheKey, JSON.toJSONString(hotArticleList));
        }


    }

    /**
     * 为每一个频道缓存热点较高的30条文章
     * @param hotArticleVoList
     */
    private void cacheTagToRedis(List<HotArticleVo> hotArticleVoList) {
        // 查询所有的频道
        ResponseResult<List<AdChannel>> channelsResult = adminFeign.selectChannels();
        if (!channelsResult.checkCode()) {
            throw new CustomException(AppHttpCodeEnum.REMOTE_SERVER_ERROR);
        }
        // 将Data数据转为对象集合
        List<AdChannel> channelList = channelsResult.getData();
        channelList.forEach(
                channel->{
                    List<HotArticleVo> articleByChannel = hotArticleVoList.stream().filter(articleVo ->
                            channel.getId().equals(articleVo.getChannelId())
                    ).collect(Collectors.toList());
                    sortAndCache(articleByChannel,ArticleConstants.HOT_ARTICLE_FIRST_PAGE+channel.getId());
                }
        );


        //
    }

    /**
     *      排序并保存到Redis中
     * @param
     * @param
     */
    private void sortAndCache(List<HotArticleVo> articleVoList, String cacheKey) {
        List<HotArticleVo> hotArticleVoList = articleVoList.stream().sorted(Comparator.comparing(HotArticleVo::getScore).reversed())
                .limit(30)
                .collect(Collectors.toList());
        redisTemplate.opsForValue().set(cacheKey,JSON.toJSONString(hotArticleVoList));

    }

    /**
     *  计算列表中文章的热点值  并封装到HotArticleVo的list中
     * @param articleList
     * @return
     */
    private List<HotArticleVo> computeArticleScore(List<ApArticle> articleList) {
        List<HotArticleVo> hotArticleVoStream = articleList.stream().map(apArticle ->
                {
                    HotArticleVo hotArticleVo = new HotArticleVo();
                    BeanUtils.copyProperties(apArticle,hotArticleVo);
                    //  计算热点值
                    Integer hotArticle= computerScore(apArticle);
                    hotArticleVo.setScore(hotArticle);
                    return hotArticleVo;

                }
                ).collect(Collectors.toList());
        return hotArticleVoStream;
    }

    /**
     *      计算热点值
     * @param apArticle
     * @return
     */
    private Integer computerScore(ApArticle apArticle) {
        int score=0;
        // 阅读 1
        if (apArticle.getViews() != null) {
            score += apArticle.getViews() * ArticleConstants.HOT_ARTICLE_VIEW_WEIGHT;
        }
        // 点赞 3
        if (apArticle.getLikes() != null) {
            score += apArticle.getLikes() * ArticleConstants.HOT_ARTICLE_LIKE_WEIGHT;
        }
        // 评论 5
        if (apArticle.getComment() != null) {
            score += apArticle.getComment() * ArticleConstants.HOT_ARTICLE_COMMENT_WEIGHT;
        }
        // 收藏 8
        if (apArticle.getCollection() != null) {
            score += apArticle.getCollection() * ArticleConstants.HOT_ARTICLE_COLLECTION_WEIGHT;
        }

        return score;

    }
}
